import processing.net.*;

/**
 * WiiFlash経由でWiiRemoteを使うためのライブラリ
 */
class Wiimote {
  Wiimote(PApplet applet) {
    String host;
    if (System.getProperty("os.name").startsWith("Windows ")
        && System.getProperty("os.version").startsWith("6.")) {
      host = "[::1]";
    } else {
      host = "127.0.0.1";
    }
    int port = 0x4A54;
    client = new Client(applet, host, port);
  }
  /**
   * WiiRemoteからデータを取得します
   */
  void update() {
    boolean dataHasCome = false;
    while (client.available() > 80) {
      if (buffer == null) {
        buffer = new int[80];
      }
      for (int i = 0; i < 80; i++) {
        buffer[i] = client.read();
      }
      dataHasCome = true;
    }
    if (buffer == null || !dataHasCome) {
      return;
    }
    bufferPos = 1;
    batteryLevel = (float)readByte() / 0xC8;
    updateButtons(readShort());
    x = readFloat();
    y = readFloat();
    z = readFloat();
    extensionType = readByte();
    updateNunchuk();
    updateClassicController();
    updateBalanceBoard();
  }
  
  /** 拡張タイプ */
  int extensionType;
  /** 各ボタン */
  Button one = new Button();
  Button two = new Button();
  Button a = new Button();
  Button b = new Button();
  Button plus = new Button();
  Button minus = new Button();
  Button home = new Button();
  Button up = new Button();
  Button down = new Button();
  Button left = new Button();
  Button right = new Button();
  /** バッテリーの残量 */
  float batteryLevel;
  /** 加速度 */
  float x, y, z;
  /** ヌンチャクの情報。検出されなかった場合はnull */
  Nunchuk nunchuk;
  /** クラシックコントローラの情報。検出されなかった場合はnull */
  ClassicController classicController;
  /** バランスボードの情報。検出されなかった場合にはnull */
  BalanceBoard balanceBoard;
  
  // 振動
  private boolean rumble;
  void setRumble(boolean newValue) {
    if (rumble != newValue) {
      rumble = newValue;
      byte[] outbuf = new byte[] {
        0, (byte)0x72, (byte)(newValue ? 0x31 : 0x30), 0x0A,
      };
      client.write(outbuf);
    }
  }

  Client client;
  int[] buffer;
  int bufferPos;
  
  int readByte() {
    return buffer[bufferPos++];
  }
  int readShort() {
    return (buffer[bufferPos++] << 8) | buffer[bufferPos++];
  }
  float readFloat() {
    return Float.intBitsToFloat((buffer[bufferPos++] << 24)
        | (buffer[bufferPos++] << 16)
        | (buffer[bufferPos++] << 8)
        | buffer[bufferPos++]);
  }
  
  private void updateButtons(int state) {
    one.set(((state >> 15) & 1) != 0);
    two.set(((state >> 14) & 1) != 0);
    a.set(((state >> 13) & 1) != 0);
    b.set(((state >> 12) & 1) != 0);
    plus.set(((state >> 11) & 1) != 0);
    minus.set(((state >> 10) & 1) != 0);
    home.set(((state >> 9) & 1) != 0);
    up.set(((state >> 8) & 1) != 0);
    down.set(((state >> 7) & 1) != 0);
    right.set(((state >> 6) & 1) != 0);
    left.set(((state >> 5) & 1) != 0);
  }
  
  private void updateNunchuk() {
    boolean hasNunchuk = extensionType == 1;
    if (hasNunchuk) {
      if (nunchuk == null) {
        nunchuk = new Nunchuk();
      }
      nunchuk.update(this);
    } else {
      nunchuk = null;
    }
  }
  private void updateClassicController() {
    boolean hasClassicController = extensionType == 2;
    if (hasClassicController) {
      if (classicController == null) {
        classicController = new ClassicController();
      }
      classicController.update(this);
    } else {
      classicController = null;
    }
  }
  private void updateBalanceBoard() {
    boolean hasBalanceBoard = extensionType == 3;
    if (hasBalanceBoard) {
      if (balanceBoard == null) {
        balanceBoard = new BalanceBoard();
      }
      balanceBoard.update(this);
    } else {
      balanceBoard = null;
    }
  }
  
  class Button {
    public boolean pressed;
    public boolean pushed;
    public void set(boolean value) {
      if (!pressed && !pushed && value) {
        pushed = true;
      } else {
        pushed = false;
      }
      pressed = value;
    }
  }
  /** ヌンチャクについての情報 */
  class Nunchuk {
    public Button cButton = new Button();
    public Button zButton = new Button();
    public float stickX;
    public float stickY;
    public float x;
    public float y;
    public float z;
    public void update(Wiimote wiimote) {
      int buttonState = wiimote.readByte();
      cButton.set(((buttonState >> 4) & 1) != 0);
      zButton.set(((buttonState >> 3) & 1) != 0);
      stickX = wiimote.readFloat();
      stickY = wiimote.readFloat();
      x = wiimote.readFloat();
      y = wiimote.readFloat();
      z = wiimote.readFloat();
    }
  }
  /** クラシックコントローラについての情報（未実装） */
  class ClassicController {
    public void update(Wiimote wiimote) {
    }
  }
  /** バランスボードについての情報 */
  class BalanceBoard {
    public float topLeftKg;
    public float topRightKg;
    public float bottomLeftKg;
    public float bottomRightKg;
    public float totalKg;
    public void update(Wiimote wiimote) {
      bottomLeftKg = wiimote.readFloat();
      bottomRightKg = wiimote.readFloat();
      topLeftKg = wiimote.readFloat();
      topRightKg = wiimote.readFloat();
      totalKg = wiimote.readFloat();
    }
  }
}

